/*
 * mox_comm_c.c
 *
 *  Created on: Jul 11, 2010
 *      Author: jmonk
 */
#include <xs1.h>
#include <stdlib.h>
#include "mox.h"
#include "mox_c.h"
#include "mox_comm.h"
#include "mox_slave.h"

#ifndef FLASHER
#pragma stackfunction 5
mox_msg *mox_comm_msg_recv_thd(chanend c)
{
	mox_outct(c, MOX_COMM_RD);
	return mox_comm_msg_recv(c);
}

void mox_comm_msg_recv_ref(chanend c, mox_msg *msg)
{
	unsigned char *p = (unsigned char *)msg;
	unsigned size = mox_chan_in_wd(c) - sizeof(mox_msg);
	unsigned char *data;
	unsigned i;
	data = malloc(size);

	if (size && (!p || !data)) {
		for (i = 0; i < (size + sizeof(mox_msg)); i++) {
			mox_chan_in(c);
		}
		if (p) free(p);
		if (data) free(data);
		return;
	}

	for (i = 0; i < sizeof(mox_msg); i++) {
		p[i] = mox_chan_in(c);
	}

	for (i = 0; i < size; i++) {
		data[i] = mox_chan_in(c);
	}
	((mox_msg *)p)->content = data;

	return;
}

mox_msg *mox_comm_msg_recv(chanend c)
{
	mox_msg *p = malloc(sizeof(mox_msg));

	mox_comm_msg_recv_ref(c, p);

	return p;
}

#pragma stackfunction 5
void mox_comm_msg_send_thd(chanend c, mox_msg msg)
{
	mox_outct(c, MOX_COMM_WR);
	mox_comm_msg_send(c, msg);
	return;
}

void mox_comm_msg_send(chanend c, mox_msg msg)
{
	unsigned i;

	mox_chan_out_wd(c, msg.size + sizeof(mox_msg));
	for (i = 0; i < sizeof(mox_msg); i++) {
		mox_chan_out(c, ((unsigned char *)&msg)[i]);
	}

	for (i = 0; i < msg.size; i++) {
		mox_chan_out(c, msg.content[i]);
	}
}

void mox_comm_connect(chanend control, chanend slave, unsigned slocal)
{
	unsigned tmp;
	unsigned i;
	unsigned ct;
	unsigned slave_dest;
	unsigned control_dest;

	asm("getd %0,res[%1]":"=r"(slave_dest):"r"(slave));
	asm("getd %0,res[%1]":"=r"(control_dest):"r"(control));

	while (1) {
		while (!mox_testct(control)) mox_chan_in(control);
		if ((tmp = mox_inct(control)) == MOX_COMM_RD) {
			/*while (mox_testct(slave)) {
				tmp = mox_inct(slave);
				slave_handle_ct(slave, tmp);
			}*/
			if (slocal) {
				tmp = 0;
				do {
					if (tmp) slave_handle_ct(slave, tmp);
					mox_chan_out_wd(slave, TH_ACK);
				} while ((tmp = mox_chan_in_wd(slave)) != MOX_MSG);
				mox_comm_msg_send(control, *(mox_msg *)mox_chan_in_wd(slave));
				mox_chan_out_wd(slave, 1);
			} else {
				ct = mox_chan_in_wd(slave);
				mox_chan_out_wd(slave, ct);
				for (i = 0; i < ct; i++) {
					tmp = mox_chan_in(slave);
					mox_chan_out(control, tmp);
				}
			}
		} else if (tmp == MOX_COMM_WR) {
			/*while (mox_testct(control)) {
				tmp = mox_inct(control);
				slave_handle_ct(control, tmp);
			}*/
			if (slocal) {
				tmp = 0;
				do {
					if (tmp) slave_handle_ct(slave, tmp);
					mox_chan_out_wd(slave, MOX_MSG);
				} while ((tmp = mox_chan_in_wd(slave)) != TH_ACK);
				mox_chan_out_wd(slave, (unsigned)mox_comm_msg_recv(control));
			} else {
				ct = mox_chan_in_wd(control);
				mox_chan_out_wd(control, ct);
				for (i = 0; i < ct; i++) {
					tmp = mox_chan_in(control);
					mox_chan_out(slave, tmp);
				}
			}
		} else if (tmp == MOX_COMM_CMP) {
			return;
		}
	}
	return;
}

unsigned mox_req_com(chanend cslave, unsigned thread)
{
	mox_msg *msg = malloc(sizeof(mox_msg));
	if (!msg) return 0;

	msg->type = MOX_REQ_THD;
	msg->content = (unsigned char *)malloc(sizeof(unsigned));
	if (msg->content) {
		msg->size = sizeof(unsigned);
		((unsigned *)msg->content)[0] = thread;
	} else {
		msg->size = 0;
	}

	mox_outct(cslave, MOX_MSG);
	mox_comm_msg_send(cslave, *msg);

	if (msg->content) free(msg->content);
	free(msg);
	msg = mox_comm_msg_recv(cslave);

	if (!msg || (msg->type != MOX_ACK)){
		if (msg && msg->content) free(msg->content);
		if (msg) free(msg);
		return 0;
	}
	if (msg->content) free(msg->content);
	free(msg);
	return 1;
}

unsigned mox_end_com(chanend cslave)
{
	mox_outct(cslave, MOX_COMM_CMP);
}

//#ifndef FLASHER
chanend mox_create_chan_remote(chanend chan_mox, int first)
{
	register int tmp;
	register int tmp2;

	asm("getr %0,0x2":"=r"(tmp):/*no inputs*/);
	if (first) {
		mox_chan_out_wd(chan_mox,tmp);
		tmp2 = mox_chan_in_wd(chan_mox);
	} else {
		tmp2 = mox_chan_in_wd(chan_mox);
		mox_chan_out_wd(chan_mox,tmp);
	}
	asm("setd res[%0],%1":/*no outputs*/:"r"(tmp), "r"(tmp2));


	return tmp;
}

void mox_free_chanend(chanend a)
{
	asm("freer res[%0]":/*no outputs*/:"r"(a));
	return;
}

void mox_create_chan_local(chanend *a, chanend *b)
{
	register int tmp;
	register int tmp2;

	asm("getr %0,0x2":"=r"(tmp):/*no inputs*/);
	asm("getr %0,0x2":"=r"(tmp2):/*no inputs*/);
	asm("setd res[%0],%1":/*no outputs*/:"r"(tmp), "r"(tmp2));
	asm("setd res[%0],%1":/*no outputs*/:"r"(tmp2), "r"(tmp));

	*a = tmp;
	*b = tmp2;
	return;
}
//#endif


void mox_clear_chans(mox_master_state *state)
{
	int i;
	for (i = 0; i < NUM_CORES; i++) {
		mox_chan_in_noblock(state->chan_mox[i]);
	}
	for (i = 0; i < NUM_CORES; i++) {
		mox_chan_in_noblock(state->chan_mox[i]);
	}
}

unsigned mox_testct(chanend channel)
{
	unsigned val;
	asm("testct %0,res[%1]":"=r"(val):"r"(channel));
	return val;
}

void mox_outct(chanend channel, unsigned val)
{
	asm("outct res[%0],%1"::"r"(channel), "r"(val));
}

unsigned mox_inct(chanend channel)
{
	unsigned val;
	asm("inct %0,res[%1]":"=r"(val):"r"(channel));
	return val;
}
#endif
